#include <stdio.h>
#include <math.h>
#include <string.h>
#include "slave.h"

__thread_local volatile unsigned long get_reply[2],put_reply[2];
__thread_local double A_lhs[2][BLKX][BLKY];
__thread_local double A_rhs[2][BLKX+2][BLKY+2];
__thread_local_fix int my_task_id,my_next_task_id,my_id,my_task_num,my_num_x,my_num_y;

extern double A[2][SIZEX][SIZEY];
extern int thread_num, iter, N, ntilex, ntiley;
extern int startj[64], starti[64];
extern int  ITER;
#define alpha_d 0.0876
#define beta_d  0.0765

/*
void while_tmp(volatile unsigned long *A,volatile unsigned *B)
{
    //printf("begin");
    while((A)!=(B));
    //printf("end");
}*/


#ifdef PROFILING
__thread_local volatile unsigned long dma_start1,dma_end1,dma_start2,dma_end2,dma_start3,dma_end3,dma_start4,dma_end4,dma_start5,dma_end5;
__thread_local volatile unsigned long comp_start,comp_end;
extern unsigned long slave_comp_count[64];
extern unsigned long slave_dma_count[64];
unsigned long rtc_()
{
  unsigned long rpcc;
  asm volatile("rcsr  %0,4":"=r"(rpcc));
  return rpcc;
}
#endif

void func()
{
     int i,j,ii,jj,k,next_i,next_j;//N ~ average num tiles that each thread assigned
     int dest,src;
     int my_iter,thread_nums;
     int stride,tsize,bsize,idma;
     int dma_get_num[2],dma_put_num[2];
     int index,last,next;
     thread_nums=thread_num;
     my_id = athread_get_id(-1);
     my_num_x=ntilex;
     my_num_y=ntiley;
     if(my_id>=thread_nums)  //threads just for synchronize
     {
        my_task_num=0;
     }
     else if(my_id >= ((my_num_x * my_num_y)%thread_nums) )
     {
        my_task_num = (my_num_x * my_num_y) /thread_nums;
     }
     else 
     {
        my_task_num=(my_num_x * my_num_y) /thread_nums + 1;
     }
     for(my_iter=0; my_iter<ITER; my_iter++)
     {
         //printf("coming\n");
        dest = my_iter % 2 ;
        src = 1-dest;
        my_task_id=my_id;
        i = my_task_id / (my_num_y) * BLKX;
        j = my_task_id % (my_num_y) * BLKY;
        index=0;
        get_reply[index]=0;
        dma_get_num[index]=0;
         //update data
#ifdef PROFILING
  dma_start1=rtc_();
#endif
        bsize=(BLKY+2)*8;
        stride=(SIZEY-(BLKY+2))*8;  //stride: from blocktail to blockhead 
        tsize= bsize*(BLKX+2);
        athread_get(PE_MODE,&A[src][i][j]
                 , &A_rhs[index][0][0]
                 , tsize,(void*)&get_reply[index]
                 , 0, stride, bsize);
#ifdef PROFILING
  dma_end1=rtc_();
  slave_dma_count[my_id]+=(dma_end1-dma_start1);
  dma_end1=dma_start1=0;
#endif
        dma_get_num[index]++;
        for (k=0; k<my_task_num; k++)
        {
           index=k%2;
           next=(k+1)%2;
           last=next;
           my_task_id=my_id+k*(thread_nums);
           i = my_task_id / my_num_y * BLKX;
           j = my_task_id % my_num_y * BLKY;
           if(k<my_task_num-1)
           {
             my_next_task_id=my_task_id+thread_nums;
             next_i = my_next_task_id / my_num_y * BLKX;
             next_j = my_next_task_id % my_num_y * BLKY;
#ifdef PROFILING
  dma_start2=rtc_();
#endif
             get_reply[next]=0;
             dma_get_num[next]=0;
             bsize=(BLKY+2)*8;
             stride=(SIZEY-(BLKY+2))*8;  //stride: from blocktail to blockhead 
             tsize= bsize*(BLKX+2);
             athread_get(PE_MODE,&A[src][next_i][next_j]
                 , &A_rhs[next][0][0]
                 , tsize,(void*)&get_reply[next]
                 , 0, stride, bsize);
             dma_get_num[next]++;
#ifdef PROFILING
  dma_end2=rtc_();
  slave_dma_count[my_id]+=(dma_end2-dma_start2);
  dma_end2=dma_start2=0;
#endif
           }
#ifdef PROFILING
  dma_start3=rtc_();
#endif
           while(get_reply[index]!=dma_get_num[index]);
           //while_tmp(get_reply[index],dma_get_num[index]);
#ifdef PROFILING
  dma_end3=rtc_();
  slave_dma_count[my_id]+=(dma_end3-dma_start3);
  dma_end3=dma_start3=0;
  comp_start=rtc_();
#endif
         //compute 
         for(ii = 1; ii < BLKX+1; ++ii) 
           for(jj = 1; jj < BLKY+1; ++jj) 
           {
                A_lhs[index][ii-1][jj-1] = alpha_d * (A_rhs[index][ii][jj]) + \
                     beta_d * (A_rhs[index][ii-1][jj] + A_rhs[index][ii+1][jj] +\
                     A_rhs[index][ii][jj-1] + A_rhs[index][ii][jj+1]);
           }
#ifdef PROFILING
  comp_end=rtc_();
  slave_comp_count[my_id]+=((comp_end-comp_start));  
  dma_start4=rtc_();
#endif
         //write back
         put_reply[index]=0;
         dma_put_num[index]=0;
         stride = (SIZEY-BLKY)*8;
         bsize = BLKY*8;
         tsize = bsize*BLKX;
         athread_put(PE_MODE,&A_lhs[index][0][0]
                     , &A[dest][i+1][j+1]
                     ,tsize,(void*)&put_reply[index],stride,bsize);
         dma_put_num[index]++;
         if(k>0){
            while(put_reply[last]!=dma_put_num[last]);
            //while_tmp(put_reply[last],dma_put_num[last]);
         }
#ifdef PROFILING
  dma_end4=rtc_();
  slave_dma_count[my_id]+=((dma_end4-dma_start4));  
  dma_end4=dma_start4=0;
#endif
        }//k
#ifdef PROFILING
  dma_start5=rtc_();
#endif        
     while(put_reply[index]!=dma_put_num[index]);
#ifdef PROFILING
  dma_end5=rtc_();
  slave_dma_count[my_id]+=((dma_end5-dma_start5));
  dma_end5=dma_start5=0;  
#endif
  //printf("last tmp\n");
     //while_tmp(put_reply[index],dma_put_num[index]);
     //printf("last tmp over\n");

     //printf("syn is begin\n");
     athread_syn(ARRAY_SCOPE,0xffff);
     //printf("syn is over\n");
  }//iter
}
